package yagwl.service.demo;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Logger;

import yagwl.service.util.svc.CacheService;
import yagwl.service.util.svc.Context;
import yagwl.service.util.svc.ServiceInvocationHandler;
import yagwl.service.util.svc.CacheService.CacheLoaderCallback;

// GWT-RPC creates this once and reuses; hence not thread-safe
// all member variables should be static
public class CommerceServiceImpl implements CommerceService {

  private static final Logger log = Logger.getLogger(CommerceServiceImpl.class
      .getName());
  private static CacheService cacheSvc;
  private static CategoryCacheLoaderCallback categoryCallback;
  private static ProductCacheLoaderCallback productCallback;
  private static CommerceServiceImpl svc = new CommerceServiceImpl();

  private CommerceServiceImpl() {
    if (cacheSvc != null)
      return;
    cacheSvc = CacheService.getInstance();
    productCallback = this.new ProductCacheLoaderCallback();
    categoryCallback = this.new CategoryCacheLoaderCallback();
    cacheSvc.registerCacheLoaderCallback(ProductEntity.class.getSimpleName(),
        productCallback);
    cacheSvc.registerCacheLoaderCallback(
        ProductCategoryEntity.class.getSimpleName(), categoryCallback);
  }

  public static CommerceService getInstance() {
    InvocationHandler handler = new ServiceInvocationHandler(svc);
    CommerceService proxy = (CommerceService) Proxy.newProxyInstance(svc
        .getClass().getClassLoader(), svc.getClass().getInterfaces(), handler);
    return proxy;
  }

  private class CategoryCacheLoaderCallback implements CacheLoaderCallback {
    @Override
    public List<?> loadList(List<Long> objectIds) {
      return null;
    }

    @Override
    public Object load(Long objectId) {
      Context ctx = Context.getCurrentInstance();
      ProductCategoryDao catDao = new ProductCategoryDao(ctx.getDaoContext());
      ProductCategoryEntity cat = catDao.getCategoryTree();
      return cat;
    }
  }

  private class ProductCacheLoaderCallback implements CacheLoaderCallback {
    @Override
    public List<?> loadList(List<Long> objectIds) {
      return null;
    }

    @Override
    public Object load(Long objectId) {
      Context ctx = Context.getCurrentInstance();
      ProductDao prdDao = new ProductDao(ctx.getDaoContext());
      ProductEntity prd = prdDao.getProduct(objectId);
      return prd;
    }
  }

  /*
   * (non-Javadoc)
   * 
   * @see yagwl.service.demo.CommerceService#getCategoryTree()
   */
  public ProductCategoryEntity getCategoryTree() {
    ProductCategoryEntity rootCategory = (ProductCategoryEntity) cacheSvc.get(
        ProductCategoryEntity.class.getSimpleName(), 0L);
    return rootCategory;
  }

  /*
   * @seeyagwl.service.demo.CommerceService#createCategory(yagwl.service.demo.
   * ProductCategoryEntity)
   */
  public ProductCategoryEntity createCategory(ProductCategoryEntity category) {
    Context ctx = Context.getCurrentInstance();
    ProductCategoryDao catDAO = new ProductCategoryDao(ctx.getDaoContext());
    ProductCategoryEntity cat = catDAO.createCategory(category);
    cacheSvc.invalidate(ProductCategoryEntity.class.getSimpleName(), 0L);
    return cat;
  }

  /*
   * (non-Javadoc)
   * 
   * @seeyagwl.service.demo.CommerceService#updateCategory(yagwl.service.demo.
   * ProductCategoryEntity)
   */
  public void updateCategory(ProductCategoryEntity category) {
    Context ctx = Context.getCurrentInstance();
    ProductCategoryDao catDAO = new ProductCategoryDao(ctx.getDaoContext());
    catDAO.updateCategory(category);
    cacheSvc.invalidate(ProductCategoryEntity.class.getSimpleName(), 0L);
  }

  /*
   * (non-Javadoc)
   * 
   * @see
   * yagwl.service.demo.CommerceService#deleteCategorySubTree(java.lang.String)
   */
  public void deleteCategorySubTree(String categoryId) {
    Context ctx = Context.getCurrentInstance();
    ProductCategoryDao catDAO = new ProductCategoryDao(ctx.getDaoContext());
    catDAO.deleteCategorySubTree(ProductCategoryEntity.decryptId(categoryId));
    cacheSvc.invalidate(ProductCategoryEntity.class.getSimpleName(), 0L);
  }

  /*
   * (non-Javadoc)
   * 
   * @see yagwl.service.demo.CommerceService#addProduct(java.lang.String,
   * java.lang.String)
   */
  public void addProduct(String categoryId, String productId) {
    Context ctx = Context.getCurrentInstance();
    ProductCategoryDao catDAO = new ProductCategoryDao(ctx.getDaoContext());
    catDAO.addProduct(ProductCategoryEntity.decryptId(categoryId),
        ProductEntity.decryptId(productId));
    cacheSvc.invalidate(ProductCategoryEntity.class.getSimpleName(), 0L);
  }

  /*
   * (non-Javadoc)
   * 
   * @see yagwl.service.demo.CommerceService#removeProduct(java.lang.String,
   * java.lang.String)
   */
  public void removeProduct(String categoryId, String productId) {
    Context ctx = Context.getCurrentInstance();
    ProductCategoryDao catDAO = new ProductCategoryDao(ctx.getDaoContext());
    catDAO.removeProduct(ProductCategoryEntity.decryptId(categoryId),
        ProductEntity.decryptId(productId));
    cacheSvc.invalidate(ProductCategoryEntity.class.getSimpleName(), 0L);
  }

  /*
   * (non-Javadoc)
   * 
   * @see yagwl.service.demo.CommerceService#getProducts()
   */
  public List<ProductEntity> getProducts() {
    Context ctx = Context.getCurrentInstance();
    ProductDao prdDAO = new ProductDao(ctx.getDaoContext());
    return prdDAO.getProducts();
  }

  /*
   * (non-Javadoc)
   * 
   * @seeyagwl.service.demo.CommerceService#createProduct(yagwl.service.demo.
   * ProductEntity)
   */
  public ProductEntity createProduct(ProductEntity product) {
    Context ctx = Context.getCurrentInstance();
    ProductDao prdDAO = new ProductDao(ctx.getDaoContext());
    return prdDAO.createProduct(product);
  }

  /*
   * (non-Javadoc)
   * 
   * @seeyagwl.service.demo.CommerceService#updateProduct(yagwl.service.demo.
   * ProductEntity)
   */
  public void updateProduct(ProductEntity product) {
    Context ctx = Context.getCurrentInstance();
    ProductDao prdDao = new ProductDao(ctx.getDaoContext());
    prdDao.updateProduct(product);
  }

  /*
   * (non-Javadoc)
   * 
   * @see yagwl.service.demo.CommerceService#deleteProduct(java.lang.String)
   */
  public void deleteProduct(String productId) {
    Context ctx = Context.getCurrentInstance();
    ProductDao prdDao = new ProductDao(ctx.getDaoContext());
    prdDao.deleteProduct(ProductEntity.decryptId(productId));
  }

  /*
   * (non-Javadoc)
   * 
   * @see yagwl.service.demo.CommerceService#addToCart(java.lang.String, int)
   */
  public CartEntity addToCart(String productIdStr, int quantity) {
    Context ctx = Context.getCurrentInstance();
    CartDao cartDao = new CartDao(ctx.getDaoContext());
    CartEntity cart = (CartEntity) cartMap.get(ctx.getSessionId());
    Long productId = Long.parseLong(productIdStr);
    if (cart == null) {
      // TODO P4 get user cart for logged-in user
      cart = cartDao.createCart(productId, quantity);
      cartMap.put(ctx.getSessionId(), cart);
    } else {
      cart = cartDao.addToCart(cart, productId, quantity);
    }

    // put product detail in cart
    ProductEntity prd = (ProductEntity) cacheSvc.get(
        ProductEntity.class.getSimpleName(), productId);
    for (int i = 0; i < cart.getCartLineIds().size(); i++) {
      CartLineEntity cartLine = cart.getCartLines().get(i);
      if (productId.equals(cartLine.getProductId())) {
        cartLine.setProduct(prd);
        break;
      }
    }
    return cart;
  }

  /*
   * (non-Javadoc)
   * 
   * @see yagwl.service.demo.CommerceService#removeFromCart(java.lang.String)
   */
  public CartEntity removeFromCart(String productId) {
    Context ctx = Context.getCurrentInstance();
    CartEntity cart = (CartEntity) cartMap.get(ctx.getSessionId());
    if (cart == null)
      return new CartEntity();
    CartDao cartDao = new CartDao(ctx.getDaoContext());
    cart = cartDao.removeFromCart(cart, Long.parseLong(productId));
    return cart;
  }

  // TODO P1.5 use memcache
  private static HashMap<String, CartEntity> cartMap = new HashMap<String, CartEntity>();

  /*
   * (non-Javadoc)
   * 
   * @see yagwl.service.demo.CommerceService#getCart()
   */
  public CartEntity getCart() {
    Context ctx = Context.getCurrentInstance();
    CartEntity cart = (CartEntity) cartMap.get(ctx.getSessionId());
    if (cart == null)
      return new CartEntity();
    return cart;
  }

}
